﻿using System;
using System.Collections.Generic;
using System.Text;
using System.Collections;
using System.IO;

namespace CodePlex.Resourceful
{
    public static class Fn
    {

        public static IEnumerable<T> ToEnumerable<T>(T value)
        {
            return new T[] { value };
        }

        public static IEnumerable<TResult> Select<TSource, TResult>(IEnumerable enumerable, Converter<TSource, TResult> converter)
        {
            foreach (TSource x in enumerable)
            {
                yield return converter(x);
            }
        }


        public static IEnumerable<T> Where<T>(IEnumerable<T> input, Predicate<T> filter)
        {
            foreach (T item in input)
            {
                if (filter(item))
                {
                    yield return item;
                }
            }
        }


        public static IEnumerable<TResult> SelectWhere<TSource, TResult>(IEnumerable<TSource> input, Predicate<TSource> filter, Converter<TSource, TResult> converter)
        {
            return Fn.Select(Fn.Where<TSource>(input, filter), converter);
        }


        public static List<T> ToList<T>(IEnumerable<T> items)
        {
            return new List<T>(items);
        }


        public static Func20<T> ToFn<T>(T item)
        {
            return delegate
            {
                return item;
            };
        }


        public static string Join<T>(string separator, IEnumerable<T> values)
        {
            StringBuilder rt = new StringBuilder();

            int i = 0;
            foreach (T value in values)
            {
                if (i++ > 0)
                {
                    rt.Append(separator);
                }
                rt.Append(Convert.ToString(value));
            }

            return rt.ToString();
        }

        public static T FirstOrDefault<T>(IEnumerable<T> input, Predicate<T> filter)
        {
            foreach (T item in input)
            {
                if (filter(item))
                {
                    return item;
                }
            }
            return default(T);
        }



        public static IEnumerable<T> OrderBy<T>(IEnumerable<T> values, Comparison<T> comparison)
        {
            List<T> rt = new List<T>(values);
            rt.Sort(comparison);
            return rt;
        }
        public static IEnumerable<T> OrderBy<T>(IEnumerable<T> values)
        {
            List<T> rt = new List<T>(values);
            rt.Sort();
            return rt;
        }

        public static int Count(IEnumerable values)
        {
            int rt = 0;
            foreach (object value in values)
            {
                rt++;
            }
            return rt;
        }


        public static IEnumerable<T> OfType<T>(IEnumerable values)
        {
            foreach (object value in values)
            {
                if (value is T)
                {
                    yield return (T)value;
                }
            }
        }

        public static TResult Sum<T, TResult>(IEnumerable<T> values, Func20<TResult, T, TResult> fn)
        {
            TResult rt = default(TResult);

            foreach (T value in values)
            {
                rt = fn(rt, value);
            }

            return rt;
        }

        public static IEnumerable<T> Distinct<T>(IEnumerable<T> input)
        {
            List<T> seen = new List<T>();
            return Fn.Where(input, delegate(T item)
            {
                if (seen.Contains(item))
                {
                    return false;
                }
                seen.Add(item);
                return true;
            });
        }


        public static IEnumerable<T> Take<T>(IEnumerable<T> values, int n)
        {
            int i = 0;
            foreach (T value in values)
            {
                if (i++ >= n)
                {
                    yield break;
                }
                yield return value;
            }
        }


        public static T Max<T>(IEnumerable<T> values, Comparison<T> comparison)
        {
            bool empty = true;
            T rt = default(T);
            foreach (T value in values)
            {
                if (empty || comparison(value, rt) > 0)
                {
                    rt = value;
                }

                empty = false;
            }

            return empty ? default(T) : rt;
        }

        public static void ForEach<T>(IEnumerable<T> values, Action20<T> action)
        {
            foreach (T value in values)
            {
                action(value);
            }
        }

        public static void ForEachWithIndex<T>(IEnumerable<T> values, Action20<T, int> action)
        {
            int i = 0;
            foreach (T value in values)
            {
                action(value, i++);
            }
        }


        public static bool Any<T>(IEnumerable<T> input, Predicate<T> filter)
        {
            foreach (T value in input)
            {
                if (filter(value))
                {
                    return true;
                }
            }

            return false;
        }
        public static bool Contains<T>(IEnumerable<T> input, T item)
        {
            foreach (T value in input)
            {
                if (object.Equals(value, item))
                {
                    return true;
                }
            }

            return false;
        }




        public static IEnumerable<List<T>> Batch<T>(IEnumerable<T> values, int batchSize)
        {
            List<T> rt = null;
            foreach (T value in values)
            {
                if (rt == null)
                {
                    rt = new List<T>();
                }
                rt.Add(value);
                if (rt.Count == batchSize)
                {
                    yield return rt;
                    rt = null;
                }
            }
            if (rt != null)
            {
                yield return rt;
            }
        }



        public static IEnumerable<string> Lines(Stream inputStream)
        {
            using (StreamReader reader = new StreamReader(inputStream))
            {
                string line;
                while ((line = reader.ReadLine()) != null)
                {
                    yield return line;
                }

            }
        }


        public static IEnumerable<int> Range(int n)
        {
            for (int i = 0; i < n; i++)
            {
                yield return i;
            }
        }


        public static T[] ToArray<T>(IEnumerable<T> values)
        {
            return ToList(values).ToArray();
        }
    }
}
